bind
「要不我們都用資料綁定如何?」Svelte 如此建議。他從那張又熱又厚的綠色皮革椅子當中起身。「我剛好把它停在樹陰下。」
~節錄自《The Great Svelte:第七章》
bind
語法<input>
元素資料綁定 經過了這麼多天的介紹跟實作,相信我們對於 Svelte 當中資料的傳遞應該很熟悉了。就像在第 10 天:Svelte 元件的 Property討論過的,資料可以藉由元件 Property 的方式,從父元件 (parent component) 傳遞給子元件。同時,若是子元件 (child component) 需要改變由父元件傳遞過來的資料,則可以透過第 12 天:Svelte 的事件:事件傳遞介紹的方法,利用事件提醒父元件 (parent) 該改變資料內容了,再由父元件 (parent component) 直接修改資料。
也就是說,在 Svelte 當中,父元件 (parent component) 跟子元件 (child component) 的溝通,是藉由 Property 將資料往下傳遞,並透過往上傳遞的事件來改變父元件 (parent) 當中的資料,如圖一。
圖一、資料向下,事件向上
因此在這樣的設計方針上,就會看到父元件中的資料以 Property 的方式 property={data}
傳遞給子元件,並且有一個事件處理器 on:changeData
接受來自子元件 (child component) 的提醒去做資料更新的動作:
<!-- 在父元件當中 -->
<ChildComponent property={data} on:changeData />
而子元件當中則會看到相對應的該傳向父元件的事件產生器 dispatch('changeData', updatedProperty)
:
<!-- 在子元件當中 -->
<script>
export let property
const handleChange = () => dispatch('changeData', updatedProperty);
</script>
這樣寫是沒有什麼問題,唯一的問題就是…太麻煩了。對一個有點規模的專案,資料傳遞是這麼的重要,而且發生頻率又是非常頻繁,如果對每個需要傳遞的資料都來上這麼套,撰寫程式碼想必會變得非常辛苦。因此 Svelte 提供了一個方便的語法來處理這個工作,也就是資料綁定 bind
。
bind
語法 資料綁定 bind
的任務很簡單,就是要打破資料只能由上而下單一方向改變這件事。藉由 bind
,我們可以將父元件 (component) 當中的資料跟子元件 (child component) 的資料綁定在一起,不僅是父元件 (parent component) 的資料改變會影響子元件,還可以在子元件 (child component) 當中直接改變資料,同時將這個變化連動回到父元件 (parent component)。
<!-- 在父元件當中改用 bind -->
<ChildComponent bind:property={data} />
<ChildComponent bind:property={data} />
bind:property={data}
就能夠將父元件 (parent component) 的資料跟子元件 (child component) 的資料綁在一起了。data
是父元件 (parent component) 當中的資料,property
則是子元件 (child component) 的 Property,可以作為子元件 (child component) 自己的資料來使用。<!-- 在子元件當中改用 bind -->
<script>
export let property
const handleChange = () => property = updatedProperty;
</script>
const handleChange = () => property = updatedProperty;
property
重新賦值,就能直接改變父元件 (parent component) 當中透過 bind:property={data}
綁定在一起的 data
了。是不是很方便呢?就讓我們實際來試試看吧。
還記得我們在色票產生器的專案當中,將所有色票的資料放在主要元件 App.svelte
的變數 palettes
當中,並作為 Property 傳向子元件 Palettes.svelte
:
/src/App.svelte
<!-- 省略一些無關的 HTML 元素 -->
<Palettes {palettes} on:changeLock={handleLock} />
同時在子元件 Palettes.svelte
當中,我們接收來自主要元件 App.svelte
的 palettes
這個變數,並透過 dispatch("changeLock", id)
來讓主要元件 App.svelte
知道 palettes
該做出相對應的更新:
<script>
/* 省略一些無關的程式碼 */
import { createEventDispatcher } from "svelte";
export let palettes;
const dispatch = createEventDispatcher();
const handleClick = (id) => dispatch("changeLock", id);
</script>
<!-- 省略一些無關的 HTML 元素 -->
<div class="lock-icon" on:click={() => handleClick(id)}>
{#if locked}
<img src={lock} alt="color-locked" />
{:else}
<img src={unlock} alt="color-unlocked" />
{/if}
</div>
然後再回到主要元件 App.svelte
實作出接收 changeLock
事件的事件處理器 handleLock
:
/src/App.svelte
<script>
/* 省略一些無關的程式碼 */
const handleLock = (e) => {
console.log(e);
const targetId = e.detail;
palettes = palettes.map((palette) => {
if (palette.id === targetId) {
return { ...palette, locked: !palette.locked };
} else {
return palette;
}
});
};
</script>
經過一番折騰,我們的解鎖/上鎖功能終於做出來了。現在既然學會了 bind
,就讓我們看看如何省去這些折騰吧:
/src/App.svelte
<!-- 省略一些無關的 HTML 元素 -->
<Palettes bind:palettes={palettes} />
<Palettes bind:palettes={palettes} />
App.svelte
當中,用 bind:palettes={palettes}
將主要元件的變數 palettes
與 Palettes.svelte
這個元件的 Property palettes
綁定在一起。還記的我們在第 10 天:Svelte 元件的 Property 當中提過,只要變數跟 Property 名稱相同,就可以簡寫。在使用 bind
的時候也不例外。所以我們現在這行程式碼,其實也可以寫成:<Palettes bind:palettes />
就是這麼簡單。那讓我們進到子元件 Palettes.svelte
,來看看該做哪些修改:
/src/lib/Palettes.svelte
<script>
/* 省略一些無關的程式碼 */
// import { createEventDispatcher } from "svelte";
export let palettes;
// const dispatch = createEventDispatcher();
// const handleClick = (id) => dispatch("changeLock", id);
</script>
<!-- 省略一些無關的 HTML 元素 -->
<div class="lock-icon" on:click={() => (locked = !locked)}>
{#if locked}
<img src={lock} alt="color-locked" />
{:else}
<img src={unlock} alt="color-unlocked" />
{/if}
</div>
第三行:// import { createEventDispatcher } from "svelte";
使用了 bind
,我們就不用向主要元件發送事件提醒修改資料了,所以 createEventDispatcher
就用不到了。
第六行:// const dispatch = createEventDispatcher();
這個用不到了。
第七行:// const handleClick = (id) => dispatch("changeLock", id);
這個也用不到了。
第十一行:<div class="lock-icon" on:click={() => (locked = !locked)}>
因為 bind
已經將主要元件跟子元件的 palettes
綁定在一起,現在我們直接在子元件當中修改資料,這個改動就會連動到主要元件。所以簡簡單單的說 on:click={() => (locked = !locked)}
就可以囉!
回到主要元件 App.svelte
,有個事件處理器 handleLock
再也用不到了:
/src/App.svelte
<script>
/* 省略一些無關的程式碼 */
// const handleLock = (e) => {
// console.log(e);
// const targetId = e.detail;
// palettes = palettes.map((palette) => {
// if (palette.id === targetId) {
// return { ...palette, locked: !palette.locked };
// } else {
// return palette;
// }
// });
// };
/* 稍微修改一下隨機產生色票的程式碼 */
onMount(() => {
const intervalId = setInterval(() => {
palettes = palettes.map((palette) => {
if (palette.locked) return palette;
else
return {
...palette,
hex: generateHex(),
};
});
}, 2000);
return () => clearInterval(intervalId);
});
</script>
第三行:// const handleLock = (e) => {
辛辛苦苦作出來的 handleLock
可以功成身退了。
第十八行:palettes = palettes.map((palette) => {
跟 bind
主題無關,稍微修改一下第 21 天:Svelte 的生命週期函式:onMount
做出來,隨機產生色票的程式碼。
第十九行:if (palette.locked) return palette;
如果 palette.locked
,也就是色票處於上鎖的狀態,那就不要改變這張色票的顏色。否則的話,就隨機產生新的顏色。
圖二、輕輕鬆鬆讓色票解鎖/上鎖
學會 bind
之後,是不是覺得 Svelte 程式碼越看越親切了呢!那麼今天的內容就到這裡了,謝謝大家。